from django import forms
from django.contrib.contenttypes.models import ContentType
from satchmo_store.contact.models import Contact, AddressBook
from satchmo_tryton.models import LocalContact, ObjMapper
from satchmo_tryton.trytonrpc import Tryton, TrytonSyncFailed


__all__ = [
    'on_delete_obj_mapper',
    'on_save_obj_mapper',
    'tryton_contact_add_form_fields',
    'tryton_contact_form_postsave',
    'tryton_contact_save',
    ]


def tryton_contact_form_postsave(sender, object=None, formdata=None, form=None,
                                 **kwargs):

    if object is None:
        return

    try:
        local_contact = LocalContact.objects.get(contact=object)
    except LocalContact.DoesNotExist:
        local_contact = LocalContact(contact=object)

    local_contact.vat_number = formdata['vat_number']
    local_contact.save()


def tryton_contact_add_form_fields(sender, form=None, **kwargs):
    """Adds some extra fields to the contact form"""


    if form._contact:
        if getattr(form._contact, 'localcontact', None):
            vat_number = form._contact.localcontact.vat_number
        else:
            vat_number = ''

        billing_state = (
            form._contact and getattr(
                form._contact.billing_address, 'state', None)) or ''
        ship_state = (
            form._contact and getattr(
                form._contact.shipping_address, 'state', None)) or ''


    form.fields['vat_number'] = forms.CharField(
        label='VAT number', required=True, max_length=100, initial=vat_number)

    form.fields['state'] = forms.CharField(
        label='State', required=True, max_length=100, initial=billing_state)
    form.fields['ship_state'] = forms.CharField(
        label='State', required=False, max_length=100, initial=ship_state)


# Tryton sync.
def tryton_contact_save(contact):
    """Push all contact fields to Tryton to sync the changes."""
    tryton = Tryton()
    tryton.connect()
    wizard_name = 'satchmo.contact.sync'

    data_dict = {
        'satchmo_id': contact.id,
        'first_name': contact.first_name,
        'last_name': contact.last_name,
        'notes': contact.notes,
        'email': contact.email,
        }

    if contact.organization:
        data_dict['organization_name'] = contact.organization.name
    else:
        data_dict['organization_name'] = None

    wizard_id = tryton.wizard_create(wizard_name)
    wizard_state = tryton.wizard_execute(wizard_name, wizard_id, data_dict)
    if 'tryton_id' in wizard_state['action'].keys():
        # Return the Tryton id associated with this contact.
        return wizard_state['action']['tryton_id']

    return None


def tryton_addressbook_save(addressbook):
    """Push all addressbook fields to Tryton."""
    contact_mapper = ObjMapper.objects.get_for_object(addressbook.contact)
    if contact_mapper.tryton_id is None:
        # The contact is not yet synced, we cannot sync the addressbook yet.
        raise TrytonSyncFailed('Contact must be synced first')

    tryton = Tryton()
    tryton.connect()
    wizard_name = 'satchmo.addressbook.sync'

    data_dict = {
        'satchmo_id': addressbook.id,
        'party_id': contact_mapper.tryton_id,
        'addressee': addressbook.addressee,
        'street1': addressbook.street1,
        'street2': addressbook.street2,
        'state': addressbook.state,
        'city': addressbook.city,
        'postal_code': addressbook.postal_code,
        'country_code': addressbook.country.iso2_code,
        'is_default_shipping': addressbook.is_default_shipping,
        'is_default_billing': addressbook.is_default_billing,
    }

    wizard_id = tryton.wizard_create(wizard_name)
    wizard_state = tryton.wizard_execute(wizard_name, wizard_id, data_dict)
    if 'tryton_id' in wizard_state['action'].keys():
        # Return the Tryton id associated with this address book.
        return wizard_state['action']['tryton_id']

    return None


def tryton_localcontact_save(localcontact):
    """Push all addressbook fields to Tryton."""
    contact_mapper = ObjMapper.objects.get_for_object(localcontact.contact)
    if contact_mapper.tryton_id is None:
        # The contact is not yet synced, we cannot sync the addressbook yet.
        raise TrytonSyncFailed('Contact must be synced first')

    tryton = Tryton()
    tryton.connect()
    wizard_name = 'satchmo.localcontact.sync'

    data_dict = {
        'satchmo_id': localcontact.id,
        'party_id': contact_mapper.tryton_id,
        'vat_number': localcontact.vat_number,
    }

    wizard_id = tryton.wizard_create(wizard_name)
    wizard_state = tryton.wizard_execute(wizard_name, wizard_id, data_dict)
    if 'tryton_id' in wizard_state['action'].keys():
        # Return the Tryton id associated with this address book.
        return wizard_state['action']['tryton_id']

    return None


def on_delete_obj_mapper(sender, instance, **kwargs):

    # prevent possible recursion
    if sender == ObjMapper:
        return

    # prepare
    inst_id = instance.id
    c_type = ContentType.objects.get_for_model(sender)

    # search for obj-mapper
    mappers = ObjMapper.objects.filter(
        content_type = c_type,
        object_id = inst_id)

    # delete mappers
    for mapper in mappers:
        mapper.delete()


def on_save_obj_mapper(sender, instance, created, **kwargs):

    # prevent possible recursion
    if sender == ObjMapper:
        return

    callbacks = {
        AddressBook: tryton_addressbook_save,
        Contact: tryton_contact_save,
        LocalContact: tryton_localcontact_save,
        }

    # We may check whether the record has just been created, but it will fail
    # for objects created before this app is activated, so we always create
    # it if there isn't already one.
    try:
        mapper = ObjMapper.objects.get_for_object(instance)
    except ObjMapper.DoesNotExist:
        # There is no mapper object yet.
        mapper = ObjMapper(object=instance)
        mapper.save()

    for obj_class, callback in callbacks.iteritems():
        if isinstance(instance, obj_class):
            try:
                tryton_id = callback(instance)
            except TrytonSyncFailed:
                # The online sync failed
                mapper.is_dirty = True
                mapper.save()
            else:
                # The online sync was successful
                mapper.is_dirty = False
                assert ((tryton_id is None and mapper.tryton_id is None) or
                        (tryton_id is not None)), (
                            'tryton_id cannot be changed back to None!')
                mapper.tryton_id = tryton_id
                mapper.save()
